#include "bsp_key.h"

/* 结构体声明 */
static KEY_T s_tBtn[KEY_COUNT];
static KEY_FIFO_T s_tKey;

/* 局部函数声明 */
static void bsp_InitKeyVar(void);


/* KEY_UP按下 */
static uint8_t IsKeyDown1(void) 
{
	if(HAL_GPIO_ReadPin(KEY_UP_GPIO_Port, KEY_UP_Pin) == 1) 
		return 1;
	else 
		return 0;
}

/* KEY0按下 */
static uint8_t IsKeyDown2(void)
{
	if(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin) == 0)
		return 1;
	else 
		return 0;
}

/* KEY1按下 */
static uint8_t IsKeyDown3(void)
{
	if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 0)
		return 1;
	else 
		return 0;
}

/* KEY2按下 */
static uint8_t IsKeyDown4(void)
{
	if(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == 0)
		return 1;
	else 
		return 0;
}

/* KEY0和KEY2同时按下 */
static uint8_t IsKeyDown5(void)
{
	if((HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == 0) 
		&& (HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin) == 0))
		return 1;
	else 
		return 0;
}


void bsp_InitKey(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};

	KEY_UP_RCC_CLK_ENABLE();
	KEY0_RCC_CLK_ENABLE();

  GPIO_InitStruct.Pin = KEY0_Pin | KEY1_Pin | KEY2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(KEY0_GPIO_Port, &GPIO_InitStruct);	

	GPIO_InitStruct.Pin = KEY_UP_Pin;
	GPIO_InitStruct.Pull = GPIO_PULLDOWN;
	HAL_GPIO_Init(KEY_UP_GPIO_Port, &GPIO_InitStruct);

	bsp_InitKeyVar();
}

/* 初始化按键参数 */
static void bsp_InitKeyVar(void)
{
	uint8_t i = 0;

	// FIFO读写指针清零
	s_tKey.Read = 0;
	s_tKey.Write = 0;
	s_tKey.Read2 = 0;

	for(i=0; i<KEY_COUNT; i++)
	{
		s_tBtn[i].Count = KEY_FILTER_TIME / 2;		// 初始值设为滤波值的一半
		s_tBtn[i].LongTime = KEY_LONG_TIME;				// 长按时间
		s_tBtn[i].State = 0;											// 0表示未按下
		s_tBtn[i].RepeatSpeed = 0;								// 0表示不支持连发
		s_tBtn[i].RepeatCount = 0;								
	}

	// 按下按下函数指针
	s_tBtn[0].IsKeyDownFunc = IsKeyDown1;
	s_tBtn[1].IsKeyDownFunc = IsKeyDown2;
	s_tBtn[2].IsKeyDownFunc = IsKeyDown3;
	s_tBtn[3].IsKeyDownFunc = IsKeyDown4;
	s_tBtn[4].IsKeyDownFunc = IsKeyDown5;
}

/**
 * @brief		将键值压入FIFO缓存区
 * @param		_KeyCode: 键值,和枚举KEY_ENUM里的值对应
 * @retval	None
 * @note		
 */
void bsp_PutKey(uint8_t _KeyCode)
{
	// 按键值压入buf，保存后s_tKey.Write自加1
	s_tKey.Buf[s_tKey.Write] = _KeyCode;

	// 最多缓存10个按键事件
	if(++s_tKey.Write >= KEY_FIFO_SIZE)
	{
		s_tKey.Write = 0;
	}
}

/**
 * @brief		从FIFO中获取一个键值 
 * @param		None
 * @retval	返回获取到的键值
 * @note
 */
uint8_t bsp_GetKey(void)
{
	uint8_t ret = 0;

	// FIFO中没有键值
	if(s_tKey.Read == s_tKey.Write)
	{
		return KEY_NONE;
	}
	else
	{
		// 取出FDFO中缓存的键值
		ret = s_tKey.Buf[s_tKey.Read];

		// 取出后Read自加1,以便取下一个键值
		if(++s_tKey.Read >= KEY_FIFO_SIZE)
		{
			s_tKey.Read = 0;
		}
	}

	return ret;
}

/* 暂无用 */
uint8_t bsp_GetKey2(void)
{
	uint8_t ret;

	if (s_tKey.Read2 == s_tKey.Write)
	{
		return KEY_NONE;
	}
	else
	{
		ret = s_tKey.Buf[s_tKey.Read2];

		if (++s_tKey.Read2 >= KEY_FIFO_SIZE)
		{
			s_tKey.Read2 = 0;
		}
		return ret;
	}
}

/**
 * @brief		设置按键的参数
 * @param		_ucKeyID: 待设置的按键编码
 * @param		_LongTime: 长按时间 0表示不支持长按
 * @param		_RepeatSpeed: 连发速度
 * @retval  None
 * @note		
 */
void bsp_SetKeyParam(uint8_t _ucKeyID, uint8_t _LongTime, uint8_t _RepeatSpeed)
{
	s_tBtn[_ucKeyID].LongTime = _LongTime;				
	s_tBtn[_ucKeyID].RepeatSpeed = _RepeatSpeed;
	s_tBtn[_ucKeyID].LongCount = 0;				// 连发计数器		
}

/**
 * @brief		按键检测函数,非阻塞,需要被周期性调用
 * @param		i: 对应的按键结构体编号
 * @retval  None
 * @note
 */
static void bsp_DetectKey(uint8_t i)
{
	KEY_T *pBtn;

	pBtn = &s_tBtn[i];

	// 按键按下
	if(pBtn->IsKeyDownFunc())
	{
		// 按下后先将计数值赋为KEY_FILTER_TIME
		if(pBtn->Count < KEY_FILTER_TIME)
		{
			pBtn->Count = KEY_FILTER_TIME;
		}
		else if(pBtn->Count < 2 * KEY_FILTER_TIME)
		{
			pBtn->Count++;		// 滤波时间
		}
		else		// 滤波后
		{
			if(pBtn->State == 0)
			{
				pBtn->State = 1;
				// 保存按键按下的键值
				bsp_PutKey((uint8_t)(3 * i + 1));
			}
			// 判断长按
			if(pBtn->LongTime > 0)
			{
				if(pBtn->LongCount < pBtn->LongTime)
				{
					if(++pBtn->LongCount == pBtn->LongTime)
					{
						// 保存长按的键值
						bsp_PutKey((uint8_t)(3 * i + 3));
					}
				}
				else		// 连发
				{
					if(pBtn->RepeatSpeed > 0)
					{
						if(++pBtn->RepeatCount >= pBtn->RepeatSpeed)
						{
							pBtn->RepeatCount = 0;
							// 如果支持连发,每隔10ms * RepeatSpeed保存一次按键按下值
							bsp_PutKey((uint8_t)(3 * i + 1));
						}
					}
				}
			}
		}
	}
	else
	{
		if(pBtn->Count > KEY_FILTER_TIME)
		{
			pBtn->Count = KEY_FILTER_TIME;
		}
		else if(pBtn->Count != 0)		// 按键弹起滤波
		{
			pBtn->Count--;
		}
		else
		{
			if(pBtn->State == 1)
			{
				pBtn->State = 0;
				// 保存按键弹起的键值
				bsp_PutKey((uint8_t)(3 * i + 2));
			}
		}

		pBtn->LongCount = 0;
		pBtn->RepeatCount = 0;
	}
}

/* 按键扫描函数，需要被周期性调用,时基10ms */
void Key_Scan(void)
{
	uint8_t i = 0;

	for(i=0; i<KEY_COUNT; i++)
	{
		bsp_DetectKey(i);
	}
}

